﻿using System.Text;
using Basic.FlowApplication.Interface;
using Basic.FlowApplication.Linq;
using Basic.FlowApplication.Model;
using Basic.FlowApplication.Model.Actor;
using Basic.FlowApplication.Model.AuditItem;
using Basic.FlowCollect;
using Basic.FlowExtendService;
using Basic.FlowModel;
using Basic.FlowModel.AuditLogItem;
using Basic.FlowRemoteModel;
using Basic.FlowRemoteModel.Application.Model;
using Basic.FlowRemoteModel.FlowNode.Model;
using Basic.FormRemoteModel;
using Basic.FormRemoteModel.QueryInter.Model;
using Basic.HrRemoteModel.Emp.Model;
using WeDonekRpc.Helper;

namespace Basic.FlowApplication.lmpl
{
    internal class FlowNextStepService : IFlowNextStepService
    {

        private readonly IFormInterService _Form;
        private readonly IFlowConfig _Config;
        private readonly IEmpService _Emp;
        private readonly IFlowAuditNodeStateCollect _State;
        private readonly IFlowAuditItemLogCollect _ItemLog;
        public FlowNextStepService ( IFlowAuditNodeStateCollect nodeState,
            IFlowAuditItemLogCollect itemLog,
            IFormInterService form,
            IEmpService emp,
            IFlowConfig config )
        {
            this._ItemLog = itemLog;
            this._State = nodeState;
            this._Config = config;
            this._Emp = emp;
            this._Form = form;
        }
        private AuditFlowBody _FlowBody;
        private AuditItemLog _Log;
        private FlowUser _StartUser;
        private FlowUser _CurUser;
        private AuditItemState _RootItem;
        private readonly List<long> _EmpId = new List<long>();
        private FlowNode _BeginNode;
        private Dictionary<long, FlowNodeAuditStatus> _NodeState;
        private readonly List<long> _jumpId = new List<long>();
        public NextStepResult GetNextNode ( AuditFlowBody flow, AuditItemLog log )
        {
            this._Log = log;
            this._FlowBody = flow;
            this._BeginNode = flow.Nodes[log.NodeId];
            if ( log.CheckIsReturn() && this._IsReturnToSource(this._Log.OptItemId.Value, out AuditNodeGroup group) )
            {
                return new NextStepResult
                {
                    NextMethod = NextStepMethod.下一步,
                    Nexts = new AuditNodeGroup[] { group },
                };
            }

            this._NodeState = this._State.GetNodeStatus(log.LogId);
            this._CurUser = this._GetCurUser();
            this._StartUser = new FlowUser
            {
                CompanyId = log.BeginCompanyId,
                EmpId = log.BeginEmpId,
                DeptId = log.BeginDeptId,
                UnitId = log.BeginUnitId
            };
            if ( this._CheckNodeIsEnd(out group) == false )
            {
                if ( group == null )
                {
                    return new NextStepResult { NextMethod = NextStepMethod.无 };
                }
                else
                {
                    return new NextStepResult
                    {
                        NextMethod = NextStepMethod.下一步,
                        Nexts = new AuditNodeGroup[]
                        {
                            group
                        }
                    };
                }
            }
            NextStepResult result = new NextStepResult
            {
                NextMethod = NextStepMethod.下一步
            };
            if ( log.MarkState == FlowItemMarkState.退回 && this._GetRestore(out group) )
            {
                result.Restore = group;
                if ( this._BeginNode.BackMethod == FlowBackMethod.返回退回节点 )
                {
                    result.NextMethod = NextStepMethod.恢复之前步骤;
                    return result;
                }
                result.NextMethod = NextStepMethod.自选步骤;
            }
            AuditNodeGroup[] nexts = this._GetNextNode();
            if ( nexts.IsNull() )
            {
                result.NextMethod = result.Restore == null ? NextStepMethod.无 : NextStepMethod.恢复之前步骤;
                return result;
            }
            if ( this._jumpId.Count > 0 )
            {
                this._State.SetJumpNode(this._Log.LogId, this._jumpId.Distinct().ToArray());
            }
            result.Nexts = nexts;
            return result;
        }

        public FlowUser _GetCurUser ()
        {
            if ( ConstDic.MarkeStatus.Contains(this._Log.MarkState) && this._Log.MarkState != FlowItemMarkState.回源 )
            {
                return new FlowUser
                {
                    CompanyId = this._RootItem.CompanyId,
                    DeptId = this._RootItem.EmpByDeptId,
                    UnitId = this._RootItem.EmpByUnitId,
                    EmpId = this._RootItem.EmpId,
                };
            }
            return new FlowUser
            {
                CompanyId = this._Log.CompanyId,
                DeptId = this._Log.EmpByDeptId,
                UnitId = this._Log.EmpByUnitId,
                EmpId = this._Log.EmpId,
            };
        }
        private Dictionary<long, EmpInfo> _EmpDic;
        private AuditNodeGroup[] _GetNextNode ()
        {
            FlowNode[] toNode = this._BeginNode.ToNodeId.Convert(a => this._FilterNode(a, this._BeginNode.Id));
            if ( !toNode.IsNull() )
            {
                NodeGroup group = new NodeGroup
                {
                    NodeId = this._BeginNode.Id,
                    NodeName = this._BeginNode.NodeName,
                    OutPolicy = this._BeginNode.OutPolicy
                };
                toNode.ForEach(a =>
                {
                    this._LoadNext(a, this._BeginNode, group);
                });
                if ( group.Node.Count > 0 || group.Children.Count > 0 )
                {
                    List<AuditNodeGroup> groups = new List<AuditNodeGroup>();
                    JumpNode[] jumps = Array.Empty<JumpNode>();
                    if ( this._EmpId.Count > 0 )
                    {
                        this._EmpDic = this._Emp.GetInfos(this._EmpId.Distinct().ToArray());
                    }
                    this._LoadAuditGroup(group, groups, jumps);
                    return groups.ToArray();
                }
            }
            return null;
        }
        private void _LoadAuditGroup ( NodeGroup group, List<AuditNodeGroup> groups, JumpNode[] ignores )
        {
            if ( group.Node.Count == 0 && group.Children.Count == 0 )
            {
                this._jumpId.Add(group.NodeId);
                return;
            }
            if ( group.Ignore != null )
            {
                ignores = ignores.Add(group.Ignore);
            }
            if ( group.Node.Count > 0 )
            {
                group.Node.ForEach(c =>
                {
                    if ( c.Actor != null && !c.Actor.EmpId.IsNull() )
                    {
                        c.Actor.Emp = c.Actor.EmpId.ConvertAll(b =>
                        {
                            EmpInfo info = this._EmpDic[b];
                            return new AuditEmp
                            {
                                Dept = info.Dept,
                                EmpName = info.EmpName,
                                EmpId = info.EmpId,
                                Sex = info.Sex,
                                UserHead = info.UserHead
                            };
                        });
                    }
                });
                groups.Add(new AuditNodeGroup
                {
                    Node = group.Node.Count == 1 ? group.Node.ToArray() : group.Node.Where(c => c.NodeType != FlowNodeType.结束节点).ToArray(),
                    NodeId = group.NodeId,
                    Ignores = ignores,
                    OutPolicy = group.OutPolicy,
                    NodeName = group.NodeName
                });
            }
            if ( group.Children.Count > 0 )
            {
                group.Children.ForEach(a =>
                {
                    this._LoadAuditGroup(a, groups, ignores);
                });
            }
        }
        private void _LoadNext ( FlowNode node, FlowNode prev, NodeGroup group )
        {
            if ( node.NodeType == FlowNodeType.结束节点 )
            {
                group.Node.Add(new AuditNode
                {
                    AuditType = FlowAuditType.无,
                    IsAllowCopy = false,
                    NodeName = node.NodeName,
                    NodeType = node.NodeType,
                    ToNodeId = node.Id
                });
                return;
            }
            else if ( node.NodeType == FlowNodeType.循环处理节点 )
            {
                AuditNode next = this._GetWhileNode(node);
                if ( next != null )
                {
                    group.Node.Add(next);
                    return;
                }
            }
            else
            {
                AuditNode next = this._GetAuditNode(node, prev);
                if ( next != null )
                {
                    group.Node.Add(next);
                    return;
                }
            }
            if ( node.NodeType == FlowNodeType.子流程 || node.OutCondition != FlowOutCondition.无 )
            {
                return;
            }
            FlowNode[] toNode = node.ToNodeId.Convert(( a ) => this._FilterNode(a, prev.Id));
            if ( !toNode.IsNull() )
            {
                NodeGroup nextGroup = new NodeGroup
                {
                    NodeId = node.Id,
                    NodeName = node.NodeName,
                    OutPolicy = node.OutPolicy,
                };
                group.Ignore = new JumpNode
                {
                    NodeId = node.Id,
                    NodeName = node.NodeName
                };
                this._jumpId.Add(node.Id);
                toNode.ForEach(a =>
                {
                    this._LoadNext(a, node, nextGroup);
                });
                if ( nextGroup.Node.Count > 0 || nextGroup.Children.Count > 0 )
                {
                    group.Children.Add(nextGroup);
                }
            }
        }
        private bool _GetRestore ( out AuditNodeGroup source )
        {
            FlowNode node = this._FlowBody.Nodes[this._Log.PrevNodeId.Value];
            if ( node.BackMethod == FlowBackMethod.重新开始 )
            {
                source = null;
                return false;
            }
            AuditItemState item = this._ItemLog.Get<AuditItemState>(this._Log.OptItemId.Value);
            EmpInfo info = this._Emp.GetInfo(item.EmpId);
            AuditNode res = new AuditNode
            {
                ToNodeId = node.Id,
                NodeName = node.NodeName,
                AuditType = node.AuditType,
                NodeType = node.NodeType,
                IsAllowCopy = ( FlowAuditButton.抄送 & node.AuditButton ) == FlowAuditButton.抄送,
                Actor = new AuditActor
                {
                    FilterType = FlowEmpFilterType.指定固定人员,
                    EmpId = new long[] { item.EmpId },
                    Emp = new AuditEmp[]
                    {
                        new AuditEmp
                        {
                            Dept = info.Dept,
                            EmpName = info.EmpName,
                            EmpId = info.EmpId,
                            Sex = info.Sex,
                            UserHead = info.UserHead
                        }
                    }
                }
            };
            source = new AuditNodeGroup
            {
                NodeId = node.Id,
                NodeName = node.NodeName,
                OutPolicy = FlowNodeOutPolicy.同时下发,
                Node = new AuditNode[] { res }
            };
            return true;
        }
        private bool _CheckNodeIsEnd ( out AuditNodeGroup group )
        {
            if ( this._BeginNode.AuditType == FlowAuditType.多人会审 )
            {
                AuditStateDto[] states = this._ItemLog.Gets<AuditStateDto>(this._Log.LogId, this._BeginNode.Id);
                states = states.Filters(this._Log);
                group = null;
                if ( states.Length == 1 )
                {
                    return true;
                }
                int num = states.Count(c => c.Status == FlowAuditItemStatus.审核通过) + 1;
                if ( num == states.Length )
                {
                    return true;
                }
                else if ( this._BeginNode.MinAuditPass.HasValue == false )
                {
                    return false;
                }
                else if ( this._BeginNode.IsRatio == false && this._BeginNode.MinAuditPass.Value > states.Length )
                {
                    return num == states.Length;
                }
                else if ( this._BeginNode.IsRatio )
                {
                    num = num * 100 / states.Length;
                }
                return num >= this._BeginNode.MinAuditPass.Value;
            }
            else if ( this._BeginNode.NodeType == FlowNodeType.循环处理节点 && this._Log.NextDeptId.HasValue )
            {
                AuditNode next = this._GetWhileNode(this._BeginNode);
                if ( next != null )
                {
                    if ( !next.Actor.EmpId.IsNull() )
                    {
                        EmpInfo[] emps = this._Emp.GetInfo(next.Actor.EmpId);
                        next.Actor.Emp = emps.ConvertAll(a => new AuditEmp
                        {
                            Dept = a.Dept,
                            Sex = a.Sex,
                            EmpId = a.EmpId,
                            EmpName = a.EmpName,
                            UserHead = a.UserHead,
                        });
                    }
                    group = new AuditNodeGroup
                    {
                        Node = new AuditNode[] { next },
                        NodeId = this._BeginNode.Id,
                        NodeName = next.NodeName,
                        OutPolicy = this._BeginNode.OutPolicy,
                    };
                    return false;
                }
            }
            group = null;
            return true;
        }
        /// <summary>
        /// 当前审批项为委托 加签 转办时检查是否回源
        /// </summary>
        /// <param name="itemId"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        private bool _IsReturnToSource ( long itemId, out AuditNodeGroup source )
        {
            if ( this._Log.MarkState == FlowItemMarkState.回源 )
            {
                source = null;
                return false;
            }
            AuditItemState item = this._ItemLog.Get<AuditItemState>(itemId);
            if ( item.MarkState == FlowItemMarkState.回源 && item.Status != FlowAuditItemStatus.已委托 )
            {
                this._RootItem = item;
                source = null;
                return false;
            }
            if ( item.Status != FlowAuditItemStatus.已委托 && this._Log.MarkState != FlowItemMarkState.向前加签 )
            {
                if ( item.OptItemId.HasValue )
                {
                    return this._IsReturnToSource(item.OptItemId.Value, out source);
                }
                this._RootItem = item;
                source = null;
                return false;
            }
            EmpInfo info = this._Emp.GetInfo(item.EmpId);
            FlowNode node = this._FlowBody.Nodes[this._Log.NodeId];
            AuditNode res = new AuditNode
            {
                ToNodeId = node.Id,
                NodeName = node.NodeName,
                AuditType = node.AuditType,
                NodeType = node.NodeType,
                NextDeptId = item.NextDeptId,
                ItemId = item.Id,
                IsAllowCopy = ( FlowAuditButton.抄送 & node.AuditButton ) == FlowAuditButton.抄送,
                Actor = new AuditActor
                {
                    FilterType = FlowEmpFilterType.指定固定人员,
                    EmpId = new long[] { item.EmpId },
                    Emp = new AuditEmp[]
                            {
                                new AuditEmp
                                {
                                    Dept = info.Dept,
                                    EmpName = info.EmpName,
                                    EmpId = info.EmpId,
                                    Sex = info.Sex,
                                    UserHead = info.UserHead
                                }
                            }
                }
            };
            source = new AuditNodeGroup
            {
                NodeId = node.Id,
                NodeName = node.NodeName,
                OutPolicy = FlowNodeOutPolicy.同时下发,
                Node = new AuditNode[]
                {
                     res
                }
            };
            return true;
        }
        private AuditNode _GetWhileNode ( FlowNode node )
        {
            if ( this._NodeState[node.Id] != FlowNodeAuditStatus.待开始 && this._NodeState[node.Id] != FlowNodeAuditStatus.审核进行中 )
            {
                return null;
            }
            AuditNode res = new AuditNode
            {
                ToNodeId = node.Id,
                NodeName = node.NodeName,
                AuditType = node.AuditType,
                NodeType = node.NodeType,
                IsAllowCopy = ( FlowAuditButton.抄送 & node.AuditButton ) == FlowAuditButton.抄送
            };
            WhileFilterParam arg = node.AuditEmp.GetWhileEmpSearchArg(this._Log, this._CurUser);
            WhileEmpResult result = this._Emp.GetWhileAuditEmp(arg);
            if ( result != null && !result.EmpId.IsNull() )
            {
                res.NextDeptId = result.NextDeptId;
                res.Actor = new AuditActor
                {
                    FilterType = FlowEmpFilterType.指定固定人员,
                    EmpId = result.EmpId
                };
                this._EmpId.AddRange(result.EmpId);
                return res;
            }
            return null;
        }
        private AuditNode _GetAuditNode ( FlowNode node, FlowNode prev )
        {
            if ( this._NodeState[node.Id] != FlowNodeAuditStatus.待开始 )
            {
                return null;
            }
            AuditNode res = new AuditNode
            {
                ToNodeId = node.Id,
                NodeName = node.NodeName,
                AuditType = node.AuditType,
                NodeType = node.NodeType,
                IsAllowCopy = ( FlowAuditButton.抄送 & node.AuditButton ) == FlowAuditButton.抄送
            };
            if ( node.AuditEmp.SpecifyMode == FlowSpecifyMode.指定节点审批人 )
            {
                long[] empId = this._ItemLog.GetNodeAuditEmpId(this._Log.LogId, this._Log.RootNodeId, node.AuditEmp.FromNodeId.Value);
                if ( empId.IsNull() )
                {
                    return null;
                }
                res.Actor = new AuditActor
                {
                    Show = node.AuditEmp.Show,
                    FilterType = FlowEmpFilterType.指定固定人员,
                    EmpId = empId
                };
                this._EmpId.AddRange(empId);
            }
            else if ( node.AuditEmp.SpecifyMode == FlowSpecifyMode.当前发起人 )
            {
                res.Actor = new AuditActor
                {
                    Show = node.AuditEmp.Show,
                    FilterType = FlowEmpFilterType.指定固定人员,
                    EmpId = new long[] { this._StartUser.EmpId }
                };
                this._EmpId.Add(this._StartUser.EmpId);
            }
            else if ( node.AuditEmp.SpecifyMode == FlowSpecifyMode.自由指定 )
            {
                res.Actor = new AuditActor
                {
                    CompanyId = node.AuditEmp.CompanyId,
                    Show = node.AuditEmp.Show,
                    FilterType = FlowEmpFilterType.自由指定
                };
            }
            else if ( node.AuditEmp.SpecifyMode == FlowSpecifyMode.指定固定人员 )
            {
                res.Actor = new AuditActor
                {
                    CompanyId = node.AuditEmp.CompanyId,
                    FilterType = FlowEmpFilterType.指定固定人员,
                    Show = node.AuditEmp.Show,
                    EmpId = node.AuditEmp.EmpId,
                };
                this._EmpId.AddRange(res.Actor.EmpId);
            }
            else if ( node.AuditEmp.SpecifyMode == FlowSpecifyMode.指定表单 )
            {
                FormVal val = this._Form.GetFormValue(new FormValGetArg
                {
                    FormId = node.FormId.GetValueOrDefault(this._FlowBody.DefFormId.Value),
                    LinkType = this._Log.LinkType,
                    LinkPK = this._Log.LinkPk,
                    ColumnId = node.AuditEmp.ColId.Value,
                    Keywords = node.AuditEmp.Keywords
                });
                if ( val.IsNull )
                {
                    return null;
                }
                res.Actor = new AuditActor
                {
                    CompanyId = node.AuditEmp.CompanyId,
                    FilterType = FlowEmpFilterType.指定固定人员,
                    Show = node.AuditEmp.Show,
                    EmpId = val.DataType == FormRecordDataType.Array ? val.Vals.ConvertAll(long.Parse) : [val.LongVal.Value]
                };
                this._EmpId.AddRange(res.Actor.EmpId);
            }
            else
            {
                ActorSearchArg arg = node.AuditEmp.GetEmpSearchArg(this._CurUser, this._StartUser);
                if ( arg.EmpId.HasValue )
                {
                    res.Actor = new AuditActor
                    {
                        CompanyId = node.AuditEmp.CompanyId,
                        FilterType = FlowEmpFilterType.指定固定人员,
                        Show = node.AuditEmp.Show,
                        EmpId = new long[] { arg.EmpId.Value }
                    };
                    this._EmpId.Add(arg.EmpId.Value);
                }
                else
                {
                    SearchEmpResult result = this._Emp.SearchAuditEmp(arg, this._Config.AuditEmpMax, 1);
                    if ( result.Count == 0 )
                    {
                        return null;
                    }
                    else
                    {
                        SearchFilterParam param = result.FilterParam;
                        res.Actor = new AuditActor
                        {
                            CompanyId = param.CompanyId,
                            FilterType = FlowEmpFilterType.条件筛选,
                            DeptId = param.DeptId,
                            PostCode = param.Post,
                            Show = node.AuditEmp.Show,
                            TitleCode = param.Title,
                            IsLeader = param.IsLeader,
                            UnitId = param.UnitId
                        };
                        if ( result.Count <= this._Config.AuditEmpMax )
                        {
                            res.Actor.EmpId = result.EmpId;
                            this._EmpId.AddRange(result.EmpId);
                            if ( node.AuditType == FlowAuditType.独立审批 && result.Count == 1 )
                            {
                                res.Actor.FilterType = FlowEmpFilterType.指定固定人员;
                            }
                        }
                    }
                }
            }

            return res;
        }

        /// <summary>
        /// 检查节点是否符合条件
        /// </summary>
        /// <param name="nodeId"></param>
        /// <returns></returns>
        private FlowNode _FilterNode ( long nodeId, long prevId )
        {
            FlowNode node = this._FlowBody.Nodes[nodeId];
            if ( node.NodeType == FlowNodeType.结束节点 )
            {
                return node;
            }
            else if ( this._NodeState[nodeId] == FlowNodeAuditStatus.已跳过 )
            {
                return node;
            }
            if ( !node.IntoFormula.IsNull() )
            {
                IntoFormula formula = node.IntoFormula.FirstOrDefault(c => c.NodeId == prevId);
                if ( formula != null && !formula.Items.IsNull() )
                {
                    //formula.FormId, this._Arg.LinkType, this._Arg.LinkPk, formula.Items.ConvertAll(c => c.ColId)
                    Dictionary<long, FormVal> vals = this._Form.GetFormValues(new FormValsGetArg
                    {
                        FormId = formula.FormId,
                        LinkPK = this._Log.LinkPk,
                        LinkType = this._Log.LinkType,
                        Column = formula.Items.ConvertAll(c => new KeyValuePair<long, string>(c.ColId, c.Keywords))
                    });
                    if ( !formula.Items.IsOk(vals) )
                    {
                        return null;
                    }
                }
            }
            if ( node.IntoCondition == FlowNodeIntoCondition.无 )
            {
                return node;
            }
            if ( node.IntoCondition == FlowNodeIntoCondition.指定节点通过 )
            {
                if ( node.IntoWaitNodeId.Value != this._BeginNode.Id )
                {
                    FlowNodeAuditStatus state = this._NodeState[node.IntoWaitNodeId.Value];
                    if ( state != FlowNodeAuditStatus.已跳过 && state != FlowNodeAuditStatus.审核通过 )
                    {
                        return null;
                    }
                }
                int passNum = node.PrevNodeId.Count(c => this._NodeState[c] == FlowNodeAuditStatus.审核通过 || this._NodeState[c] == FlowNodeAuditStatus.已跳过) + 1;
                if ( passNum < node.IntoMinPassNum.Value )
                {
                    return null;
                }
            }
            else if ( node.IntoCondition == FlowNodeIntoCondition.部分通过 )
            {
                int passNum = node.PrevNodeId.Count(c => this._NodeState[c] == FlowNodeAuditStatus.审核通过 || this._NodeState[c] == FlowNodeAuditStatus.已跳过) + 1;
                if ( passNum < node.IntoMinPassNum.Value )
                {
                    return null;
                }
            }
            else if ( node.IntoCondition == FlowNodeIntoCondition.全部通过 )
            {
                if ( node.PrevNodeId.IsExists(c => c != this._BeginNode.Id && ( this._NodeState[c] == FlowNodeAuditStatus.审核进行中 || this._NodeState[c] == FlowNodeAuditStatus.待开始 )) )
                {
                    return null;
                }
            }
            return node;
        }
    }
}
